using System;
using System.Collections.Generic;
using System.Text;
using System.Data;

using NUnit.Framework;

using NSimpleDB.Service.Contract;

namespace test.Service.VistaDb.Blackbox
{
    [TestFixture]
    public class test_Query
    {
        #region single predicate tests
        [Test]
        public void test1PredicateWith1Comp()
        {
            System.IO.File.Delete("temp.vdb3");
            using (ISimpleDBService s = new NSimpleDB.Service.VistaDb.VistaDbSimpleDBService("temp.vdb3"))
            {
                s.CreateDomain("d");

                s.PutAttributes("d", "1", new SimpleDBAttribute("a", "a"));
                s.PutAttributes("d", "2", new SimpleDBAttribute("a", "a"),
                                          new SimpleDBAttribute("a", "b"));
                s.PutAttributes("d", "3", new SimpleDBAttribute("a", "a"),
                                          new SimpleDBAttribute("a", "b"),
                                          new SimpleDBAttribute("a", "c"));

                s.PutAttributes("d", "4", new SimpleDBAttribute("b", "b"));
                s.PutAttributes("d", "5", new SimpleDBAttribute("b", "b1"));
                s.PutAttributes("d", "6", new SimpleDBAttribute("b", "b12"));
                s.PutAttributes("d", "7", new SimpleDBAttribute("b", "b123"));


                string[] itemNames;
                itemNames = s.Query("d", "['a'='c']");
                Assert.AreEqual(1, itemNames.Length);
                Assert.IsTrue(this.CheckExpectedItemNames("3", itemNames));

                itemNames = s.Query("d", "['a'='b']");
                Assert.AreEqual(2, itemNames.Length);
                Assert.IsTrue(this.CheckExpectedItemNames("23", itemNames));

                itemNames = s.Query("d", "['a'='a']");
                Assert.AreEqual(3, itemNames.Length);
                Assert.IsTrue(this.CheckExpectedItemNames("123", itemNames));


                itemNames = s.Query("d", "['a'>'a']");
                Assert.AreEqual(2, itemNames.Length);
                Assert.IsTrue(this.CheckExpectedItemNames("23", itemNames));

                itemNames = s.Query("d", "['a'<'c']");
                Assert.AreEqual(3, itemNames.Length);
                Assert.IsTrue(this.CheckExpectedItemNames("123", itemNames));


                itemNames = s.Query("d", "['a' != 'a']");
                Assert.AreEqual(2, itemNames.Length);
                Assert.IsTrue(this.CheckExpectedItemNames("23", itemNames));
                        // 2 items because each needs an attribute 'a' whose value is not equal to 'a'

                itemNames = s.Query("d", "[not 'a' = 'a']");
                Assert.AreEqual(6, itemNames.Length);
                Assert.IsTrue(this.CheckExpectedItemNames("234567", itemNames));
                        // 6 items because each needs some attribute whose name (!) is not equal to 'a' or (!) whose 'a'-attribute value is not equal to 'a'

                itemNames = s.Query("d", "['b' starts-with 'b1']");
                Assert.AreEqual(3, itemNames.Length);
                Assert.IsTrue(this.CheckExpectedItemNames("567", itemNames));

                itemNames = s.Query("d", "['b' starts-with 'b12']");
                Assert.AreEqual(2, itemNames.Length);
                Assert.IsTrue(this.CheckExpectedItemNames("67", itemNames));
            }
        }

        
        [Test]
        public void test1PredicateWithORComp()
        {
            System.IO.File.Delete("temp.vdb3");
            using (ISimpleDBService s = new NSimpleDB.Service.VistaDb.VistaDbSimpleDBService("temp.vdb3"))
            {
                s.CreateDomain("d");

                s.PutAttributes("d", "1", new SimpleDBAttribute("a", "a"));
                s.PutAttributes("d", "2", new SimpleDBAttribute("a", "a"),
                                          new SimpleDBAttribute("a", "b"));
                s.PutAttributes("d", "3", new SimpleDBAttribute("a", "a"),
                                          new SimpleDBAttribute("a", "b"),
                                          new SimpleDBAttribute("a", "c"));
                s.PutAttributes("d", "4", new SimpleDBAttribute("a", "d"));

                string[] itemNames;
                itemNames = s.Query("d", "['a'='a' or 'a'='b']");
                Assert.AreEqual(3, itemNames.Length);
                Assert.IsTrue(this.CheckExpectedItemNames("123", itemNames));

                itemNames = s.Query("d", "['a'='b' or 'a'='d']");
                Assert.AreEqual(3, itemNames.Length);
                Assert.IsTrue(this.CheckExpectedItemNames("234", itemNames));

                itemNames = s.Query("d", "['a'='b' or 'a'='d' or 'a'='x']");
                Assert.AreEqual(3, itemNames.Length);
                Assert.IsTrue(this.CheckExpectedItemNames("234", itemNames));
            }
        }

        
        [Test]
        public void test1PredicateWithANDComp()
        {
            System.IO.File.Delete("temp.vdb3");
            using (ISimpleDBService s = new NSimpleDB.Service.VistaDb.VistaDbSimpleDBService("temp.vdb3"))
            {
                s.CreateDomain("d");

                s.PutAttributes("d", "1", new SimpleDBAttribute("a", "a"));
                s.PutAttributes("d", "2", new SimpleDBAttribute("a", "a"),
                                          new SimpleDBAttribute("a", "b"));
                s.PutAttributes("d", "3", new SimpleDBAttribute("a", "a"),
                                          new SimpleDBAttribute("a", "b"),
                                          new SimpleDBAttribute("a", "c"));
                s.PutAttributes("d", "4", new SimpleDBAttribute("a", "d"));

                string[] itemNames;
                itemNames = s.Query("d", "['a'='a' and 'a'='b']");
                Assert.AreEqual(2, itemNames.Length);
                Assert.IsTrue(this.CheckExpectedItemNames("23", itemNames));

                itemNames = s.Query("d", "['a'='b' and 'a'='d']");
                Assert.AreEqual(0, itemNames.Length);

                itemNames = s.Query("d", "['a'='a' and 'a'='b' and 'a'='c']");
                Assert.AreEqual(1, itemNames.Length);
                Assert.IsTrue(this.CheckExpectedItemNames("3", itemNames));
            }
        }


        [Test]
        public void test1PredicateWithANDORComp()
        {
            System.IO.File.Delete("temp.vdb3");
            using (ISimpleDBService s = new NSimpleDB.Service.VistaDb.VistaDbSimpleDBService("temp.vdb3"))
            {
                s.CreateDomain("d");

                s.PutAttributes("d", "1", new SimpleDBAttribute("a", "a"));
                s.PutAttributes("d", "2", new SimpleDBAttribute("a", "a"),
                                          new SimpleDBAttribute("a", "b"));
                s.PutAttributes("d", "3", new SimpleDBAttribute("a", "a"),
                                          new SimpleDBAttribute("a", "b"),
                                          new SimpleDBAttribute("a", "c"));
                s.PutAttributes("d", "4", new SimpleDBAttribute("a", "d"));
                s.PutAttributes("d", "5", new SimpleDBAttribute("a", "e"));

                string[] itemNames;
                itemNames = s.Query("d", "['a'='d' or 'a'='b' and 'a'='c' or 'a'='e']");
                Assert.AreEqual(3, itemNames.Length);
                Assert.IsTrue(this.CheckExpectedItemNames("345", itemNames));
            }
        }
        #endregion

        #region multi predicate tests
        [Test]
        public void testNPredicatesWithUNION()
        {
            System.IO.File.Delete("temp.vdb3");
            using (ISimpleDBService s = new NSimpleDB.Service.VistaDb.VistaDbSimpleDBService("temp.vdb3"))
            {
                s.CreateDomain("d");

                s.PutAttributes("d", "1", new SimpleDBAttribute("a", "a"));
                s.PutAttributes("d", "2", new SimpleDBAttribute("a", "a"),
                                          new SimpleDBAttribute("a", "b"));
                s.PutAttributes("d", "3", new SimpleDBAttribute("a", "a"),
                                          new SimpleDBAttribute("a", "b"),
                                          new SimpleDBAttribute("a", "c"));
                s.PutAttributes("d", "4", new SimpleDBAttribute("a", "d"));

                string[] itemNames;
                itemNames = s.Query("d", "['a'='a'] union ['a'='b']");
                Assert.AreEqual(3, itemNames.Length);
                Assert.IsTrue(this.CheckExpectedItemNames("123", itemNames));

                itemNames = s.Query("d", "['a'='b'] union ['a'='d']");
                Assert.AreEqual(3, itemNames.Length);
                Assert.IsTrue(this.CheckExpectedItemNames("234", itemNames));

                itemNames = s.Query("d", "['a'='b'] union ['a'='d'] union ['a'='x']");
                Assert.AreEqual(3, itemNames.Length);
                Assert.IsTrue(this.CheckExpectedItemNames("234", itemNames));
            }
        }


        [Test]
        public void testNPredicatesWithINTERSECTION()
        {
            System.IO.File.Delete("temp.vdb3");
            using (ISimpleDBService s = new NSimpleDB.Service.VistaDb.VistaDbSimpleDBService("temp.vdb3"))
            {
                s.CreateDomain("d");

                s.PutAttributes("d", "1", new SimpleDBAttribute("a", "a"));
                s.PutAttributes("d", "2", new SimpleDBAttribute("a", "a"),
                                          new SimpleDBAttribute("a", "b"));
                s.PutAttributes("d", "3", new SimpleDBAttribute("a", "a"),
                                          new SimpleDBAttribute("a", "b"),
                                          new SimpleDBAttribute("a", "c"));
                s.PutAttributes("d", "4", new SimpleDBAttribute("a", "d"));

                string[] itemNames;
                itemNames = s.Query("d", "['a'='a'] intersection ['a'='b']");
                Assert.AreEqual(2, itemNames.Length);
                Assert.IsTrue(this.CheckExpectedItemNames("23", itemNames));

                itemNames = s.Query("d", "['a'='b'] intersection ['a'='d']");
                Assert.AreEqual(0, itemNames.Length);

                itemNames = s.Query("d", "['a'='a'] intersection ['a'='b'] intersection ['a'='c']");
                Assert.AreEqual(1, itemNames.Length);
                Assert.IsTrue(this.CheckExpectedItemNames("3", itemNames));
            }
        }


        [Test]
        public void testNPredicatesWithUNIONINTERSECTION()
        {
            System.IO.File.Delete("temp.vdb3");
            using (ISimpleDBService s = new NSimpleDB.Service.VistaDb.VistaDbSimpleDBService("temp.vdb3"))
            {
                s.CreateDomain("d");

                s.PutAttributes("d", "1", new SimpleDBAttribute("a", "a"),
                                          new SimpleDBAttribute("b", "b"));
                s.PutAttributes("d", "2", new SimpleDBAttribute("a", "a"),
                                          new SimpleDBAttribute("a", "b"),
                                          new SimpleDBAttribute("c", "c"));
                s.PutAttributes("d", "3", new SimpleDBAttribute("a", "a"),
                                          new SimpleDBAttribute("a", "b"),
                                          new SimpleDBAttribute("a", "c"),
                                          new SimpleDBAttribute("b", "b"));
                s.PutAttributes("d", "4", new SimpleDBAttribute("a", "d"),
                                          new SimpleDBAttribute("b", "c"));
                s.PutAttributes("d", "5", new SimpleDBAttribute("a", "a"),
                                          new SimpleDBAttribute("b", "c"),
                                          new SimpleDBAttribute("c", "d"));

                string[] itemNames;
                itemNames = s.Query("d", "['a'='a' or 'a'='b'] intersection ['b'='b' or 'b'='c']");
                Assert.AreEqual(3, itemNames.Length, "found: " + string.Join(",", itemNames));
                Assert.IsTrue(this.CheckExpectedItemNames("135", itemNames));

                itemNames = s.Query("d", "['c'='c'] union ['a'='b'] intersection ['b'='b'] union ['b'='c']");
                Assert.AreEqual(4, itemNames.Length, "found: " + string.Join(",", itemNames));
                Assert.IsTrue(this.CheckExpectedItemNames("2345", itemNames));

                itemNames = s.Query("d", "['c'='c'] intersection ['a'='b'] union ['a'='a'] intersection ['b'='c']");
                Assert.AreEqual(2, itemNames.Length, "found: " + string.Join(",", itemNames));
                Assert.IsTrue(this.CheckExpectedItemNames("25", itemNames));
            }
        }
        #endregion


        [Test]
        public void testPagedQueryResults()
        {
            System.IO.File.Delete("temp.vdb3");
            using (ISimpleDBService s = new NSimpleDB.Service.VistaDb.VistaDbSimpleDBService("temp.vdb3"))
            {
                s.CreateDomain("d");

                s.PutAttributes("d", "1", new SimpleDBAttribute("a", "a"));
                s.PutAttributes("d", "2", new SimpleDBAttribute("a", "b"));
                s.PutAttributes("d", "3", new SimpleDBAttribute("a", "c"));

                string[] itemNames;
                string nextToken = null;

                Assert.AreEqual(0, s.Query("d", "['a'='x']", 10, ref nextToken).Length);
                Assert.IsNull(nextToken);

                itemNames = s.Query("d", "['a'>='a']", 3, ref nextToken);
                Assert.AreEqual(3, itemNames.Length);
                Assert.IsNull(nextToken);

                itemNames = s.Query("d", "['a'>='a']", 2, ref nextToken);
                Assert.AreEqual(2, itemNames.Length);
                Assert.IsNotNull(nextToken);
                itemNames = s.Query("d", "['a'>='a']", ref nextToken);
                Assert.AreEqual(1, itemNames.Length);
                Assert.IsNull(nextToken);

                itemNames = s.Query("d", "['a'>='a']", 1, ref nextToken);
                Assert.AreEqual(1, itemNames.Length);
                Assert.IsNotNull(nextToken);
                itemNames = s.Query("d", "['a'>='a']", 1, ref nextToken);
                Assert.AreEqual(1, itemNames.Length);
                Assert.IsNotNull(nextToken);
                itemNames = s.Query("d", "['a'>='a']", ref nextToken);
                Assert.AreEqual(1, itemNames.Length);
                Assert.IsNull(nextToken);
            }
        }


        private bool CheckExpectedItemNames(string expectedItemNames, string[] itemNames)
        {
            foreach(char expectedItemName in expectedItemNames)
            {
                bool expectedFound = false;
                foreach (string foundItemName in itemNames)
                    if (foundItemName == expectedItemName.ToString()) expectedFound = true;
                if (!expectedFound) return false;
            }
            return true;
        }
    }
}
